Skip to content

feat(extensions): add GitHub handler extension and env var access#62

Merged
ezynda3 merged 2 commits into
masterfrom
feat/60-github-handler-extension
Jun 15, 2026
Merged

feat(extensions): add GitHub handler extension and env var access#62
ezynda3 merged 2 commits into
masterfrom
feat/60-github-handler-extension

Conversation

@ezynda3

@ezynda3 ezynda3 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Description

This adds the GitHub handler — the runtime half of Kit's GitHub integration (Phase 2b of #60) — plus the small extension-runtime change it depends on.

The new examples/extensions/github-handler extension is designed to run inside a GitHub Actions runner, driven by the workflow that kit github install scaffolds. When a collaborator comments /kit <request> on an issue or PR, it parses the event from GITHUB_EVENT_PATH, enforces that the author has write/admin access (author_association in OWNER/MEMBER/COLLABORATOR), reacts 👀, gathers context (issue thread or PR diff via gh), drives the agent, posts the response back as a comment, and — if the agent left uncommitted changes — pushes a kit-agent[bot] branch and opens a PR. It is inert outside Actions (GITHUB_ACTIONS != "true"), so it is safe to keep loaded locally, and a KIT_GITHUB_DRY_RUN mode logs all gh/git side effects instead of executing them for deterministic testing.

Building this surfaced a prerequisite: Yaegi runs extensions in restricted mode where os.Getenv/os.LookupEnv/os.Environ read a virtualized environment that Kit left empty, so extensions could not read any env var. The interpreter (in both the production loader and the test harness) is now seeded with os.Environ(). This is read-only from the host's perspective — os.Setenv only mutates the extension's sandboxed copy — and is strictly weaker than the os/exec access extensions already have.

Type of Change

  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have added tests that prove my fix is effective or that my feature works
  • I have made corresponding changes to the documentation
  • New and existing unit tests pass locally with my changes

Additional Information

Part of #60 (this is one phase of the GitHub-integration epic, so it intentionally does not auto-close the umbrella issue).

New files

  • examples/extensions/github-handler/main.go — the handler extension (OnSessionStart parse/gate/drive, OnAgentEnd comment + PR)
  • examples/extensions/github-handler/main_test.go — 8 tests (permission gate, /kit parsing, issue vs PR-review-comment paths, comment posting, PR-on-dirty-tree) using dry-run mode
  • examples/extensions/github-handler/README.md — usage, env vars, options, security notes

Modified

  • internal/extensions/loader.go, pkg/extensions/test/harness.go — seed the interpreter env with os.Environ()
  • www/pages/extensions/loading.md — new "Standard library access" section documenting env-var readability and sandboxing
  • www/pages/extensions/testing.md — testing extensions that read env vars (t.Setenv before load)
  • www/pages/extensions/examples.md, www/pages/cli/commands.md, README.md, examples/extensions/README.md — index the extension and cross-reference it from the GitHub-integration docs

Backward compatibility: no breaking changes. Extensions that never read env vars are unaffected; those that previously got empty strings from os.Getenv now receive real values.

Verification: go test -race ./internal/extensions/... ./pkg/extensions/... ./examples/extensions/... and go test ./cmd/... pass; go vet ./... clean; golangci-lint run reports 0 issues; the www docs site builds cleanly.

Summary by CodeRabbit

Release Notes

  • New Features
    • Added the GitHub Handler example extension to support running as a GitHub Actions collaborator and responding to /kit triggers in issue comments and pull request review comments.
  • Documentation
    • Expanded GitHub integration docs to clarify the runtime component used for event handling, permissions, responses, and PR updates.
    • Added guidance on extension environment variable access (snapshotted at load time) and improved extension examples/testing documentation, including dry-run behavior.
  • Tests
    • Added integration-style tests covering triggers, permission gating, prompt construction, and dry-run side effects.

- add github-handler example extension that runs Kit as a GitHub
  collaborator inside Actions: parses the event, gates on
  author_association, drives the agent, posts comments, and opens PRs
- seed the Yaegi interpreter with os.Environ() in the loader and test
  harness so extensions can read env vars (e.g. GITHUB_EVENT_PATH) via
  os.Getenv/LookupEnv/Environ without mutating the host environment
- document env var access, the new extension, and env-aware testing
  across the docs site, README, and kit-extensions skill

Part of #60
@mark-iii-labs-huly

Copy link
Copy Markdown

Connected to Huly®: KIT-63

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4e9b8d0c-9ea9-4fe4-8dea-7d97cf775fa6

📥 Commits

Reviewing files that changed from the base of the PR and between 0cc3cc0 and 0575424.

📒 Files selected for processing (2)
  • examples/extensions/github-handler/main.go
  • examples/extensions/github-handler/main_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/extensions/github-handler/main_test.go
  • examples/extensions/github-handler/main.go

📝 Walkthrough

Walkthrough

Adds a new github-handler extension that runs Kit as a GitHub Actions collaborator: it parses event payloads, gates on author_association, drives the agent with issue/PR context, posts responses as comments, and opens PRs for code changes. The Yaegi interpreter is now seeded with the host process environment in both production and test harness. Matching documentation is added across READMEs and website pages.

Changes

GitHub Handler Extension and Environment Seeding

Layer / File(s) Summary
Yaegi interpreter environment seeding
internal/extensions/loader.go, pkg/extensions/test/harness.go
Both production loader and test harness now pass Env: os.Environ() to interp.New, enabling extensions to read CI-provided environment variables through a virtualized, sandboxed copy.
Data model, event parsing, and trigger extraction
examples/extensions/github-handler/main.go
Defines the /kit trigger token, bot identity constants, allowed author_association map, and all GitHub Actions event payload types. Implements loadEvent, buildTrigger (comment extraction, PR vs issue detection), and extractRequest for /kit token recognition (full-line or end-of-line only).
Init, session start, and context gathering
examples/extensions/github-handler/main.go
Implements Init to register github.dry-run option and wire OnSessionStart/OnAgentEnd. Session start loads and validates the trigger, posts an initial "eyes" reaction, gathers context from event payload and optionally from gh CLI (diffs, comment threads), builds the prompt, and drives the agent.
AgentEnd response posting and PR creation
examples/extensions/github-handler/main.go
Implements handleAgentEnd to post the agent response (or error message) as a comment, detect uncommitted changes, optionally commit as kit-agent[bot], push a timestamped branch, open a PR via gh pr create, and update emoji reactions based on success or error.
Tests
examples/extensions/github-handler/main_test.go
Nine integration tests cover handler registration, inert behavior outside Actions, authorized/unauthorized issue comments, non-/kit comments, mid-sentence mentions, PR review comment targeting, dry-run comment posting, and dry-run PR creation when the working tree is dirty.
Documentation
examples/extensions/github-handler/README.md, examples/extensions/README.md, README.md, www/pages/cli/commands.md, www/pages/extensions/examples.md, www/pages/extensions/loading.md, www/pages/extensions/testing.md
Adds the github-handler README (runtime flow, env vars, security), extension index entries, and www docs for stdlib/env access and env-variable testing patterns.

Sequence Diagram(s)

sequenceDiagram
    participant GitHubActions as GitHub Actions
    participant OnSessionStart as OnSessionStart
    participant gh as gh CLI
    participant KitAgent as Kit Agent
    participant OnAgentEnd as OnAgentEnd

    GitHubActions->>OnSessionStart: trigger (GITHUB_EVENT_PATH)
    OnSessionStart->>OnSessionStart: loadEvent → buildTrigger → auth check
    OnSessionStart->>gh: addReaction("eyes")
    rect rgba(200, 200, 255, 0.5)
    Note over OnSessionStart,gh: when not dry-run and gh exists
    OnSessionStart->>gh: fetch PR diffs and comment threads
    end
    OnSessionStart->>OnSessionStart: buildPrompt
    OnSessionStart->>KitAgent: DriveAgent(prompt)
    KitAgent->>OnAgentEnd: AgentEnd(response)
    OnAgentEnd->>gh: postComment(response or error)
    alt uncommitted changes exist
        OnAgentEnd->>OnAgentEnd: git commit as kit-agent[bot]
        OnAgentEnd->>gh: git push + gh pr create
    end
    OnAgentEnd->>gh: addReaction("rocket" or "confused")
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Poem

🐇 Hippity hop, a new extension is born,
It reads the event at the break of morn.
/kit in a comment? The agent awakes!
It checks your permissions, then action it takes.
A PR is opened, a reaction appears —
The bot-bunny delivers, to everyone's cheers! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main features: the GitHub handler extension and environment variable access support for extensions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/60-github-handler-extension

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
examples/extensions/github-handler/main.go (1)

141-143: ⚡ Quick win

Use session-scoped trigger state instead of a single package-global pointer.

activeTrigger is a single mutable slot shared across callbacks. If sessions overlap or callbacks interleave, replies/PR side effects can be associated with the wrong trigger. Store trigger state by session (with synchronization) and clear it after completion.

As per coding guidelines, "Widget/header/footer/editor state lives on the Runner with sync.RWMutex for thread safety, queried by UI via callbacks" and "Extension contexts use function fields (Print func(string), SetWidget func(WidgetConfig)) wired by closures in cmd/root.go for state management".

Also applies to: 184-184, 346-347

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/extensions/github-handler/main.go` around lines 141 - 143, The
package-global `activeTrigger` pointer is a single mutable slot shared across
all sessions and callbacks, creating a race condition when sessions overlap or
callbacks interleave. Replace this single package-level variable with a
session-scoped state management approach: use a map keyed by session identifiers
(protected by sync.RWMutex for thread safety) to store trigger state separately
for each session, following the pattern established in your coding guidelines
where state lives on the Runner with proper synchronization. At
examples/extensions/github-handler/main.go line 141-143, replace the `var
activeTrigger *trigger` declaration with a protected map structure; at line
184-184, update the code that writes to activeTrigger to instead write to the
session-keyed map entry; and at line 346-347, update the code that reads from
activeTrigger to instead read from the session-keyed map entry. Ensure the
trigger state is cleared after session completion to prevent memory leaks and
state pollution.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/extensions/github-handler/main.go`:
- Around line 449-473: The gitOutput, ghOutput, and runCmd functions use
unbounded exec.Command calls that can hang indefinitely if the git or gh
commands stall due to network issues, authentication prompts, or other blocking
conditions. To fix this, modify all three functions to use exec.CommandContext
instead of exec.Command, passing a context with an appropriate timeout (such as
a context derived from the provided ctx parameter with a reasonable duration
like 30 seconds). Ensure the timeout is applied consistently across gitOutput,
ghOutput, and runCmd to prevent workflow stalls when subprocess calls become
unresponsive.
- Around line 275-279: The extractRequest function incorrectly recognizes the
commandToken when it appears in the middle of prose with spaces around it (like
"please review /kit behavior"). Remove or modify the strings.Index check that
looks for " "+commandToken+" " anywhere in the trimmed string, as this matches
incidental mentions. Instead, only recognize the command when it appears at the
beginning of the message (using strings.HasPrefix with commandToken+" ") or at
the end (the existing strings.HasSuffix case). This ensures the handler only
triggers on actual command invocations, not mid-sentence prose containing the
token.

---

Nitpick comments:
In `@examples/extensions/github-handler/main.go`:
- Around line 141-143: The package-global `activeTrigger` pointer is a single
mutable slot shared across all sessions and callbacks, creating a race condition
when sessions overlap or callbacks interleave. Replace this single package-level
variable with a session-scoped state management approach: use a map keyed by
session identifiers (protected by sync.RWMutex for thread safety) to store
trigger state separately for each session, following the pattern established in
your coding guidelines where state lives on the Runner with proper
synchronization. At examples/extensions/github-handler/main.go line 141-143,
replace the `var activeTrigger *trigger` declaration with a protected map
structure; at line 184-184, update the code that writes to activeTrigger to
instead write to the session-keyed map entry; and at line 346-347, update the
code that reads from activeTrigger to instead read from the session-keyed map
entry. Ensure the trigger state is cleared after session completion to prevent
memory leaks and state pollution.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2e104fd3-93de-4a15-8f6a-c0cba357687a

📥 Commits

Reviewing files that changed from the base of the PR and between 7067c99 and 0cc3cc0.

📒 Files selected for processing (11)
  • README.md
  • examples/extensions/README.md
  • examples/extensions/github-handler/README.md
  • examples/extensions/github-handler/main.go
  • examples/extensions/github-handler/main_test.go
  • internal/extensions/loader.go
  • pkg/extensions/test/harness.go
  • www/pages/cli/commands.md
  • www/pages/extensions/examples.md
  • www/pages/extensions/loading.md
  • www/pages/extensions/testing.md

Comment thread examples/extensions/github-handler/main.go Outdated
Comment thread examples/extensions/github-handler/main.go Outdated
- only trigger on /kit at the start or end of a comment line, ignoring
  incidental mid-sentence mentions like "please review /kit behavior"
- bound git/gh subprocess calls with a 30s timeout via CommandContext so
  a stalled network call or auth prompt cannot hang the Actions job
- add a regression test for the mid-sentence mention case

Part of #60
@ezynda3 ezynda3 merged commit 16662ca into master Jun 15, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant